﻿using CleanArchitecture.Core;
using CleanArchitecture.Core.Configuration;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace CleanArchitecture.Infrastructure.Plugins.BaiduAddressSDK
{
    public class BaiduIpAddressDetail
    {
        public string province { get; set; }
        public string city { get; set; }
        public string district { get; set; }
        public string street { get; set; }
        public string street_number { get; set; }
        public string city_code { get; set; }
    }
    public class BaiduIpAddressPoint
    {
        public string y { get; set; }
        public string x { get; set; }
    }
    public class BaiduIpAddressContent
    {
        public BaiduIpAddressDetail address_detail { get; set; }
        public string address { get; set; }
        public BaiduIpAddressPoint point { get; set; }
    }
    public class BaiduIpAddress
    {
        //Api response Json: 
        //{
        //   "address": "CN|\u4e91\u5357|\u6606\u660e|None|CHINANET|0|0",
        //   "content": {
        //      "address_detail": {
        //	        "province": "\u4e91\u5357\u7701",
        //	        "city": "\u6606\u660e\u5e02",
        //	        "district": "",
        //	        "street": "",
        //	        "street_number": "",
        //	        "city_code": 104
        //      },
        //      "address": "\u4e91\u5357\u7701\u6606\u660e\u5e02",
        //      "point": {
        //	        "y": "2863528.35",
        //	        "x": "11434261.51"
        //      }
        //    },
        //    "status": 0
        //}
        public string address { get; set; }
        public BaiduIpAddressContent content { get; set; }
        public int status { get; set; }
        public string message { get; set; }
    }

    public class BaiduAddressSDK
    {
        private const string IpLocationUri = "http://api.map.baidu.com";
        private const string IpLocationPath = "/location/ip"; 

        //https://www.cnblogs.com/cdyy/p/9789731.html
        private static string MD5(string password)
        {
            try
            {
                System.Security.Cryptography.HashAlgorithm hash = System.Security.Cryptography.MD5.Create();
                byte[] hash_out = hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));

                var md5_str = BitConverter.ToString(hash_out).Replace("-", "");
                return md5_str.ToLower();
            }
            catch
            {

                throw;
            }
        }

        private static string UrlEncode(string str)
        {
            str = System.Web.HttpUtility.UrlEncode(str);
            byte[] buf = Encoding.ASCII.GetBytes(str);//等同于Encoding.ASCII.GetBytes(str)
            for (int i = 0; i < buf.Length; i++)
                if (buf[i] == '%')
                {
                    if (buf[i + 1] >= 'a') buf[i + 1] -= 32;
                    if (buf[i + 2] >= 'a') buf[i + 2] -= 32;
                    i += 2;
                }
            return Encoding.ASCII.GetString(buf);//同上，等同于Encoding.ASCII.GetString(buf)
        }

        private static string HttpBuildQuery(IDictionary<string, string> querystring_arrays)
        {

            StringBuilder sb = new StringBuilder();
            foreach (var item in querystring_arrays)
            {
                sb.Append(UrlEncode(item.Key));
                sb.Append("=");
                sb.Append(UrlEncode(item.Value));
                sb.Append("&");
            }
            sb.Remove(sb.Length - 1, 1);
            return sb.ToString();
        }
        #region 百度提供.net版SDK MD5算法有问题
        //private static string MD5(string password)
        //{
        //    byte[] textBytes = System.Text.Encoding.UTF8.GetBytes(password);
        //    try
        //    {
        //        System.Security.Cryptography.MD5CryptoServiceProvider cryptHandler;
        //        cryptHandler = new System.Security.Cryptography.MD5CryptoServiceProvider();
        //        byte[] hash = cryptHandler.ComputeHash(textBytes);
        //        string ret = "";
        //        foreach (byte a in hash)
        //        {
        //            ret += a.ToString("x");
        //        }
        //        return ret;
        //    }
        //    catch
        //    {
        //        throw;
        //    }
        //}

        //private static string UrlEncode(string str)
        //{
        //    str = System.Web.HttpUtility.UrlEncode(str);
        //    byte[] buf = Encoding.ASCII.GetBytes(str);//等同于Encoding.ASCII.GetBytes(str)
        //    for (int i = 0; i < buf.Length; i++)
        //        if (buf[i] == '%')
        //        {
        //            if (buf[i + 1] >= 'a') buf[i + 1] -= 32;
        //            if (buf[i + 2] >= 'a') buf[i + 2] -= 32;
        //            i += 2;
        //        }
        //    return Encoding.ASCII.GetString(buf);//同上，等同于Encoding.ASCII.GetString(buf)
        //}

        //private static string HttpBuildQuery(IDictionary<string, string> querystring_arrays)
        //{

        //    StringBuilder sb = new StringBuilder();
        //    foreach (var item in querystring_arrays)
        //    {
        //        sb.Append(UrlEncode(item.Key));
        //        sb.Append("=");
        //        sb.Append(UrlEncode(item.Value));
        //        sb.Append("&");
        //    }
        //    sb.Remove(sb.Length - 1, 1);
        //    return sb.ToString();
        //}
        #endregion
        private static string CaculateAKSN(string sk, string url, IDictionary<string, string> querystring_arrays)
        {
            var queryString = HttpBuildQuery(querystring_arrays);

            var str = UrlEncode(url + "?" + queryString + sk);

            return MD5(str);
        }
        /// <summary>
        /// DavidLee 2020/02/20 增加开启/关闭使用BaiduIpSdk方法，避免以后其他网站不使用的情况
        /// </summary>
        /// <param name="client"></param>
        /// <param name="nowaConfig"></param>
        /// <param name="loginIp"></param>
        /// <returns></returns>
        public static async Task<BaiduIpAddress> GetIpLocationAsync(HttpClient client, NowaConfig nowaConfig, IPAddress loginIp)
        {
            if (string.IsNullOrEmpty(loginIp.ToString())) return null;
            if (string.IsNullOrEmpty(nowaConfig.BaiduAddressAccessKey) || string.IsNullOrEmpty(nowaConfig.BaiduAddressSecretKey))
                return null;
            IDictionary<string, string> BaiduIpLocationQuery = new Dictionary<string, string>()
                {
                    { "ip" , loginIp.ToString() },
                    { "ak" , nowaConfig.BaiduAddressAccessKey }
                };
            string baiduSN = CaculateAKSN(nowaConfig.BaiduAddressSecretKey, IpLocationPath, BaiduIpLocationQuery);
            string baiduipLocationUrl = $"{IpLocationUri}{IpLocationPath}?ip={loginIp}&ak={nowaConfig.BaiduAddressAccessKey}&sn={baiduSN}";
            BaiduIpLocationQuery.Clear();
            //DavidLee Dont set timeout before task cancelled
            //client.Timeout = TimeSpan.FromMilliseconds(timeout <= 60 ? 60 : timeout);
            var request = new HttpRequestMessage(HttpMethod.Get, baiduipLocationUrl);
            var response = await client.SendAsync(request);
            //do something for response
            if (!response.IsSuccessStatusCode) throw new NowaException("Http Get Response Error");
            var resultStr = response.Content.ReadAsStringAsync().Result;
            BaiduIpAddress ipaddress = JsonConvert.DeserializeObject<BaiduIpAddress>(resultStr);
            //常见错误码： 1： 内部错误
            //           2： Ip Illegal
            //           211: APP SN校验失败
            //           其他：参见 http://lbs.baidu.com/index.php?title=webapi/ip-api
            if (ipaddress != null && ipaddress.status == 211) throw new NowaException($"Baidu Ip Address SDK Error: {ipaddress.message}");
            else if (ipaddress != null && ipaddress.status != 0) return null;
            else
            {
                request.Dispose();
                return ipaddress;
            }
        }
    }

}
